#include <stdlib.h>

#include "atom.h"
#include "configuration.h"
#include "parse.h"
#include "predicates.h"
#include "system.h"

Atom::AtomPool *Atom::atomPool;

void deleteAtomPool () {
  delete Atom::atomPool;
}

// Creates in the pool
Atom *Atom::create (const std::string &name) {
  if(!atomPool) {
    atomPool = new AtomPool;
    atexit (&deleteAtomPool);
  }

  const char *nameBuffer = name.c_str ();
  if(atomPool->atoms.find (nameBuffer) != atomPool->atoms.end ())
    return atomPool->atoms[nameBuffer];
  else {
    // If pool is quite contented, clean up unused atoms if any
    if(atomPool->atoms.size () > atomPool->cleanSize)
      atomPool->clean ();

    Atom *newAtom = new Atom (name);
    return atomPool->atoms[newAtom->name.c_str ()] = newAtom;
  }
}

void Atom::AtomPool::clean () {
  mutex.lock ();

  Atoms::iterator iter;
  for(iter = atoms.begin (); iter != atoms.end (); iter++)
    if((*iter).second.node->referenceCount <= 1) // At least one reference from pool
      atoms.erase (iter);

  cleanSize = atoms.size () * 3 / 2; // Next point for cleaning
  mutex.unlock ();
}

char *encode3 (unsigned char u) {
  static char s[4] = "%  ";
  static char hex[] = "0123456789ABCDEF";
  s[1] = hex[u >> 4];
  s[2] = hex[u & 15];
  return s;
}

bool needEncode (unsigned char u) {
  return u >= 128 || // Multibyte stuff
      (u < 32 && u != 9) || // Control characters except tab
      u == '%'; // Escape character itself
}

std::string Atom::dump (int) {
  if(name.length () == 0) // Nil atom
    return "$";
  else if(isInteger (name))
    return "'" + name + "'";
  else {
    bool needQuote = false;
    std::string ret;
    ret.reserve (name.size ());

    for(std::string::size_type i = 0; i < name.length (); i++) {
      if(name[i] != '.' && // Quote if not alphanumeric
          (name[i] < '0' || name[i] > '9') && (name[i] < 'A' || name[i] > 'Z')
          && (name[i] < 'a' || name[i] > 'z'))
        needQuote = true;

      if(name[i] == '\'')
        ret += "\'\'";
      else if(needEncode (name[i])) {
        needQuote = true; // Physically unneeded, logically better be here
        ret += encode3 (name[i]);
      } else
        ret += name[i];
    }

    return needQuote ? '\'' + ret + '\'' : ret;
  }
}

std::string String::dump (int) {
  std::string ret;
  ret.reserve (name.size () + 2);
  ret = '\"';

  for(std::string::size_type i = 0; i < name.length (); i++)
    if(name[i] == '\"')
      ret += "\"\"";
    else if(needEncode (name[i]))
      ret += encode3 (name[i]);
    else
      ret += name[i];
  ret += '\"';
  return ret;
}

std::string String::display (int) {
  return name;
}

std::string Number::dump (int) {
  char buffer[20];
  snprintf (buffer, 20, "%d", value);
  if(value >= 0)
    return buffer;
  else
    return std::string ("(") + buffer + ")";
}

std::string Number::display (int precedence) {
  return dump (precedence);
}

Numbers::Numbers () {
  for(int i = 0; i < nDefinedNumbers; i++)
    numbers[i] = new Number (i);
}

class intCall : public Prover {
predicateName("int")

  intCall () : state (0) {}

  bool prove () {
    if(state > 0)
      binder.undo (system->trail);

    Node *final = tuple->nodes[1].final ();
    if(typeid(*final) == typeid(Number))
      return state-- == 0;
    else if(typeid(*final) == typeid(Reference)) {
      binder.capture (system->trail);
      binder.bind (*final, *Number::create (state), system->trail);
      return ++state > 0; // We only return positives
    }
    else
      return false;
  }

  int state;
  Binder binder;
  System *system;
  Tuple *tuple;
  void set (Tuple &t, System &s) {
    tuple = &t;
    system = &s;
  }
};

ProverFactoryRegistrar<intCall, Tuple::ID, 2> intRegistrar;

class isAtom : public OnceProver {
predicateName("is.atom")

  bool proveOnce () {
    return typeid(*tuple->nodes[1].final ()) == typeid(Atom);
  }

  Tuple *tuple;
  void set (Tuple &t, System&) {
    tuple = &t;
  }
};

ProverFactoryRegistrar<isAtom, Tuple::ID, 2> isAtomRegistrar;

class isNumber : public OnceProver {
predicateName("is.number")

  bool proveOnce () {
    return typeid(*tuple->nodes[1].final ()) == typeid(Number);
  }

  Tuple *tuple;
  void set (Tuple &t, System&) {
    tuple = &t;
  }
};

ProverFactoryRegistrar<isNumber, Tuple::ID, 2> isNumberRegistrar;

class isString : public OnceProver {
predicateName("is.string")

  bool proveOnce () {
    return typeid(*tuple->nodes[1].final ()) == typeid(String);
  }

  Tuple *tuple;
  void set (Tuple &t, System&) {
    tuple = &t;
  }
};

ProverFactoryRegistrar<isString, Tuple::ID, 2> isStringRegistrar;
